package com.example.designpattern.behavioral.iterator;

import lombok.NonNull;

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

/**
 * 设想一个场景：我们有一个类中存在一个列表。这个列表需要提供给外部类访问，但我们不希望外部类修改其中的数据。
 * <p>
 * 通常来说，将成员变量提供给外部类访问有两种方式：
 * <p>
 * 将此列表设置为 public 变量；
 * 添加 getData() 方法，返回此列表。
 * 但这两种方式都有一个致命的缺点，它们无法保证外部类不修改其中的数据。外部类拿到 data 对象后，可以随意修改列表内部的元素，这会造成极大的安全隐患。
 * <p>
 * 那么有什么更好的方式吗？使得外部类只能读取此列表中的数据，无法修改其中的任何数据，保证其安全性。
 * <p>
 * 分析可知，我们可以通过提供两个方法实现此效果：
 * <p>
 * 提供一个 String next() 方法，使得外部类可以按照次序，一条一条的读取数据；
 * 提供一个 boolean hasNext() 方法，告知外部类是否还有下一条数据。
 */
public class MyList implements Iterable<String> {
    /**
     * 由于没有给外部类暴露 data 成员变量，所以我们可以保证数据是安全的。
     *
     * 但这样的实现还有一个问题：当遍历完成后，hasNext() 方法就会一直返回 false，无法再一次遍历了，所以我们必须在一个合适的地方把 index 重置成 0。
     *
     * 在哪里重置比较合适呢？
     *
     */
    private List<String> data = Arrays.asList("a", "b", "c");
    private int index = 0;

    public String next() {
        // 返回数据后，将 index 加 1，使得下次访问时返回下一条数据
        return data.get(index++);
    }

    public boolean hasNext() {
        return index < data.size();
    }

    // 每次生成一个新的迭代器，用于遍历列表
    @NonNull
    @Override
    public Iterator<String> iterator() {
        /**
         * 每次遍历时生成一个迭代器，将 index 变量放到迭代器中。由于每个迭代器都是新生成的，所以每次遍历时的 index 自然也就被重置成 0 了
         */
        return new Itr();
    }

    /**
     * 迭代器实现的是Iterator接口，不是Iterable接口，MyList才需要实现Iterable接口
     */
    private class Itr implements Iterator<String> {
        private int index = 0;

        @Override
        public boolean hasNext() {
            return index < data.size();
        }

        @Override
        public String next() {
            return data.get(index++);
        }
    }
}

